/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <mc/log.h>

#include "interface/network_adapter/fault_status.h"

namespace dev {

bool NetworkAdapter_FaultStatus::update_fault_state_code_by_ncsi() {
    ncsi_fault_state_code_t fault_state_code = {};
    bool ret = m_ncsi_over_mctp_huawei->get_fault_state_code(DEFAULT_PACKAGE_ID, DEFAULT_CHANNEL_ID,
                                                             &fault_state_code);
    if (!ret) {
        elog("Error: Failed to update fault state code");
        return false;
    }
    if (fault_state_code.health_state != 0) {
        ilog("card is not healthy, health_state: ${health_state}, error_count: ${error_count}",
             ("health_state", fault_state_code.health_state)("error_count",
                                                             fault_state_code.error_count));
    }

    std::string fault_code;
    for (int i = 0; i < fault_state_code.error_count; i++) {
        fault_code += std::to_string(fault_state_code.error_code[i]) + ",";
    }
    FaultCode.set_value(fault_code);
    // 故障告警只用第一个故障码
    if (fault_state_code.error_count > 0) {
        FaultState.set_value(fault_state_code.error_code[0]);
    } else {
        FaultState.set_value(0);
    }
    return true;
}

void NetworkAdapter_FaultStatus::start_ncsi_update_task_huawei(ncsi_over_mctp_hw_ptr ncsi_over_mctp,
                                                               mc::milliseconds      interval) {
    m_ncsi_timer            = new mc::core::timer(this);
    m_ncsi_over_mctp_huawei = ncsi_over_mctp;
    m_ncsi_timer->timeout.connect([this]() {
        update_fault_state_code_by_ncsi();
    });
    m_ncsi_timer->set_single_shot(false);
    m_ncsi_timer->start(interval);
}

void NetworkAdapter_FaultStatus::stop_ncsi_update_task() {
    if (m_ncsi_timer) {
        m_ncsi_timer->stop();        
        m_ncsi_timer = nullptr;
    }
    if (m_ncsi_over_mctp_huawei) {
        m_ncsi_over_mctp_huawei = nullptr;
    }
}

//  ==== smbus ====
void NetworkAdapter_FaultStatus::check_netCard_Health_by_smbus() {
    // 检查网卡健康状态, 无需进行数据更新，如果Health部委0，则需要报出错误码
    auto request = mc::mutable_dict{{"opcode", 0x01}, {"expect_data_len", 1}};
    auto rsp     = m_smbus_obj->send_request(request);
    if (rsp.empty()) {
        elog("Error: Failed to check netCard Health");
        return;
    }

    struct rsp_health {
        uint8_t health_status;
    } health_rsp;

    auto health_status = reinterpret_cast<rsp_health*>(rsp.data());
    m_health_status    = health_status->health_status;
    if (health_status->health_status != 0) {
        elog("card is not in normal state, health_status is {health_status}",
             ("health_status", health_status->health_status));
        return;
    }
}

void NetworkAdapter_FaultStatus::updata_fault_code_by_smbus() {
    auto request = mc::mutable_dict{{"opcode", 0x02}};
    auto rsp     = m_smbus_obj->send_request(request);
    if (rsp.empty()) {
        elog("Error: Failed to update fault code");
        return;
    }
    // 从rsp中解析 last_code, 格式为 每两个字节组成一个fault code，根据rsp进行解析
    std::vector<uint16_t> last_code;
    for (size_t i = 0; i < rsp.size(); i += 2) {
        uint16_t fault_code = rsp[i] | (rsp[i + 1] << 8);
        last_code.push_back(fault_code);
    }
    // 比较当前的last_code与m_last_fault_code中的值是否完全一致
    if (m_health_status != 0 && last_code != m_last_fault_code) {
        m_last_fault_code = last_code;
        // 故障只保留第一个故障码
        FaultState.set_value(last_code[0]);
    }
    return;
}

void NetworkAdapter_FaultStatus::start_smbus_update_task(smbus_obj_ptr    smbus_obj,
                                                         mc::milliseconds interval) {
    if (!smbus_obj) {
        elog("Error: smbus_obj is not initialized");
        return;
    }
    // 保存传入的smbus_obj到成员变量
    m_smbus_obj = smbus_obj;

    m_smbus_timer = new mc::core::timer(this);
    m_smbus_timer->timeout.connect([this]() {
        check_netCard_Health_by_smbus();
        updata_fault_code_by_smbus();
    });
    m_smbus_timer->set_single_shot(false);
    m_smbus_timer->start(interval);
}

void NetworkAdapter_FaultStatus::stop_smbus_update_task() {
    if (m_smbus_timer) {
        m_smbus_timer->stop();
        
        m_smbus_timer = nullptr;
    }
}

} // namespace dev

MC_REFLECT(dev::NetworkAdapter_FaultStatus)